# -*- coding: utf-8 -*-
# FileName:     zaudio2.py
# Time:         23/5/15 015 下午 1:39
# Author:       Zhou Hang
# Description:  基于sounddevice, non-block模式播放音频
import queue
import sys
import threading

import numpy as np
import sounddevice as sd

from src.utils.common import DEFAULT_RECORD_DIR, gen_unique_name
from src.utils.zwave import WriteWave
from src.utils.zweb import upload_wav


class StdAudio:
    _instance = None

    def __new__(cls, *args, **kw):
        """
        实现单例
        :param args:
        :param kw:
        """
        if cls._instance is None:
            cls._instance = object.__new__(cls, *args, **kw)
        return cls._instance

    def __init__(self):
        self.save_mode = False  # 默认关闭保存模式
        self.new_record_fn = ""
        self.new_record = None  # ReadWave类型值，若打开保存模式，必不为None
        self.new_record_data = []  # 临时保存录音数据, 等录音结束后再统一保存

        self.block = []  # 主线程缓冲区
        self.blocksize = 512  # 每满blocksize就加入到buffer中
        self.buffersize = 10
        self.buffer = queue.Queue(maxsize=self.buffersize)

        self.channels = 1
        self.samplerate = 44100
        self.device = None  # None表示使用默认输出设别
        self.event = threading.Event()
        self.stream = sd.OutputStream(device=self.device,
                                      channels=self.channels,
                                      callback=self.callback,
                                      samplerate=self.samplerate,
                                      blocksize=self.blocksize,
                                      finished_callback=self.event.set
                                      )
        self.stop = False
        self.play_t = threading.Thread(target=self.open)
        self.play_t.start()

    def set_save_mode(self, switch, filename: str = ""):
        """
        调用这个函数后, play_float播放的声音会被保存到wav文件中
        :param switch: True-开启保存模式, False-关闭保存模式
        :param filename: 如果设置了这个值, 播放的声音会被保存到这个文件中，否则生成在默认位置
        """
        self.save_mode = switch
        if switch:
            print("开始录音")
            if filename == "":
                self.new_record_fn = f"{DEFAULT_RECORD_DIR}/{gen_unique_name()}.wav"
            else:
                self.new_record_fn = filename
        else:
            print("结束录音")
            self.new_record = WriteWave(self.new_record_fn)
            self.new_record.write_floats(self.new_record_data)
            # if self.new_record is not None:
            upload_wav(self.new_record_fn)
            print("录音上传成功")
            self.__finish_record()
            self.new_record = None
            self.new_record_fn = ""
            self.new_record_data = []

    def __finish_record(self):
        self.new_record.close()

    def open(self):
        with self.stream:
            self.event.wait()

    def callback(self, outdata, frames, time, status):
        if self.stop:
            print("合成器退出")
            raise sd.CallbackStop
        if status:
            print(status, file=sys.stderr)
        if status.output_underflow:
            print('Output underflow: increase blocksize?', file=sys.stderr)
            return
            # raise sd.CallbackAbort
        if self.buffer.empty():
            outdata.fill(0)
        else:
            raw_data = self.buffer.get()
            data = np.array(raw_data).reshape(-1, self.channels)
            outdata[:] = data

    def play_float(self, sample):
        """
        :param sample: float类型的数据
        :return:
        """
        if len(self.block) >= self.blocksize:
            self.buffer.put(self.block)
            if self.save_mode:
                self.new_record_data.extend(self.block)
                # 这里不能做过于耗时的操作
            self.block = []  # 这里修改并不会应影响self.buffer的值
        if sample < -1.0: sample = -1.0
        elif sample > +1.0: sample = +1.0
        self.block.append(sample)
        

    def close(self):
        self.stop = True


def main():
    pass


if __name__ == "__main__":
    main()
